/**
 * 
 */
package com.huxi.lang.local;

import java.sql.Timestamp;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.TimeZone;

import com.huxi.lang.exception.ValueParseException;

/**
 * 本地化日期接口
 * 
 * @since 1.0
 * @version 1.0
 * @author jian.wu
 */
public interface DateContext {

	/**
	 * 所属时区
	 * 
	 * @return 时区，非空。
	 * @since 1.0
	 */
	TimeZone getTimeZone();

	/**
	 * 所属时区偏移
	 * 
	 * @return 时区偏移，非空。
	 * @since 1.0
	 */
	ZoneOffset getZoneOffset();

	/**
	 * 创建日期格式对象
	 * 
	 * @param pattern 格式模板，非空。
	 * @return 日期格式对象，非空。
	 * @throws NullPointerException     非空参数为空
	 * @throws IllegalArgumentException 无效的格式模板
	 * @since 1.0
	 */
	default Format createDateFormat(String pattern) {
		SimpleDateFormat fmt = new SimpleDateFormat(pattern);
		fmt.setTimeZone(getTimeZone());
		return fmt;
	}

	/**
	 * 所属时区日期格式对象
	 * 
	 * @return 日期格式对象，非空。
	 * @since 1.0
	 */
	Format getDateFormat();

	/**
	 * 解析所属时区日期
	 * 
	 * @param text 文本，非空。
	 * @return 日期，非空。
	 * @throws NullPointerException 非空参数为空
	 * @throws ValueParseException  文本内容解析失败
	 * @since 1.0
	 */
	default Date parseDate(String text) throws ValueParseException {
		try {
			return (Date) getDateFormat().parseObject(text);
		} catch (ParseException e) {
			throw new ValueParseException(Date.class, text, e);
		}
	}

	/**
	 * 所属时区时间戳格式对象
	 * 
	 * @return 时间戳格式对象，非空。
	 * @since 1.0
	 */
	Format getTimestampFormat();

	/**
	 * 解析所属时区时间戳
	 * 
	 * @param text 文本，非空。
	 * @return 时间戳，非空。
	 * @throws NullPointerException 非空参数为空
	 * @throws ValueParseException  文本内容解析失败
	 * @since 1.0
	 */
	default Date parseTimestamp(String text) throws ValueParseException {
		try {
			return (Date) getTimestampFormat().parseObject(text);
		} catch (ParseException e) {
			throw new ValueParseException(Date.class, text, e);
		}
	}

	/**
	 * 解析所属时区日期时间
	 * 
	 * @param text 文本，非空。
	 * @return 日期时间，非空。
	 * @throws NullPointerException 非空参数为空
	 * @throws ValueParseException  文本内容解析失败
	 * @since 1.0
	 */
	default Date parseDateTime(String text) throws ValueParseException {
		Objects.requireNonNull(text);
		ParsePosition p = new ParsePosition(0);
		Date d = (Date) getDateFormat().parseObject(text, p);
		if (d != null && p.getIndex() == text.length()) {
			return d;
		}
		try {
			return (Date) getTimestampFormat().parseObject(text);
		} catch (ParseException e) {
			throw new ValueParseException(Date.class, text, e);
		}
	}

	/**
	 * 本地当前时间戳
	 * 
	 * @return 本地时间戳，非空。
	 * @since 1.0
	 */
	default Timestamp currentTimestamp() {
		return new Timestamp(System.currentTimeMillis());
	}

	/**
	 * 所属日历
	 * 
	 * @return 日历，非空。
	 * @since 1.0
	 */
	default Calendar getCalendar() {
		return Calendar.getInstance(getTimeZone());
	}

	/**
	 * 系统毫秒数是否周末
	 * 
	 * @param calendar 系统毫秒数
	 * @return 周末返回真，否则返回假。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default boolean isWeekend(long millis) {
		Calendar cal = getCalendar();
		cal.setTimeInMillis(millis);
		return DateUtils.isWeekend(cal);
	}

	/**
	 * 系统日期是否周末
	 * 
	 * @param calendar 系统日期，非空。
	 * @return 周末返回真，否则返回假。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default boolean isWeekend(java.util.Date date) {
		Objects.requireNonNull(date);
		Calendar cal = getCalendar();
		cal.setTime(date);
		return DateUtils.isWeekend(cal);
	}

	/**
	 * 本地日期是否周末
	 * 
	 * @param calendar 本地日期，非空。
	 * @return 周末返回真，否则返回假。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default boolean isWeekend(LocalDate localDate) {
		return DateUtils.isWeekend(localDate);
	}

	/**
	 * 本地时间戳是否周末
	 * 
	 * @param calendar 本地时间戳，非空。
	 * @return 周末返回真，否则返回假。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default boolean isWeekend(LocalDateTime localDateTime) {
		return DateUtils.isWeekend(localDateTime.toLocalDate());
	}

	/**
	 * 本地日期转换为系统秒数
	 * 
	 * @param localDate 本地日期，非空。
	 * @return 系统秒数
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default long toEpochSecond(LocalDate localDate) {
		return DateUtils.toEpochSecond(localDate, getZoneOffset());
	}

	/**
	 * 本地日期转换为系统毫秒数
	 * 
	 * @param localDate 本地日期，非空。
	 * @return 系统毫秒数
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default long toEpochMilli(LocalDate localDate) {
		return DateUtils.toEpochMilli(localDate, getZoneOffset());
	}

	/**
	 * 本地日期转化为系统日期
	 * 
	 * @param localDate 本地日期，非空。
	 * @return 系统日期，非空。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default java.sql.Date toDate(LocalDate localDate) {
		return DateUtils.toZoneDate(localDate, getZoneOffset());
	}

	/**
	 * 系统毫秒数转化为本地日期
	 * 
	 * @param millis 系统毫秒数
	 * @return 本地日期，非空。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default LocalDate toLocalDate(long millis) {
		return DateUtils.toLocalDate(millis, getZoneOffset());
	}

	/**
	 * 系统日期转化为本地日期
	 * 
	 * @param date 系统日期，非空。
	 * @return 本地日期，非空。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default LocalDate toLocalDate(java.util.Date date) {
		return DateUtils.toLocalDate(date, getZoneOffset());
	}

	/**
	 * 系统毫秒数转化为本地日期时间
	 * 
	 * @param millis 系统毫秒数
	 * @return 本地日期时间，非空。
	 * @since 1.0
	 */
	default LocalDateTime toLocalDateTime(long millis) {
		return DateUtils.toLocalDateTime(millis, getZoneOffset());
	}

	/**
	 * 系统毫秒数转化为本地日期时间
	 * 
	 * @param date 系统日期，非空。
	 * @return 本地日期时间，非空。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default LocalDateTime toLocalDateTime(java.util.Date date) {
		return DateUtils.toLocalDateTime(date.getTime(), getZoneOffset());
	}

	/**
	 * 系统毫秒数自动截断为天精度（0时0分0秒0毫秒）毫秒数
	 * 
	 * @param millis 系统毫秒数
	 * @return 截断后毫秒数
	 * @since 1.0
	 */
	default long dayInMillis(long millis) {
		return DateUtils.toDayEpochMilli(millis, getZoneOffset());
	}

	/**
	 * 系统日期自动截断为天精度（0时0分0秒0毫秒）毫秒数
	 * 
	 * @param date 系统日期，非空。
	 * @return 截断后毫秒数
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default long dayInMillis(java.util.Date date) {
		return DateUtils.toDayEpochMilli(date, getZoneOffset());
	}

	/**
	 * 本地日期自动截断为天精度（0时0分0秒0毫秒）毫秒数
	 * 
	 * @param localDate 本地日期，非空。
	 * @return 截断后毫秒数
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default long dayInMillis(LocalDate localDate) {
		return DateUtils.toEpochMilli(localDate, getZoneOffset());
	}

	/**
	 * 系统日期自动截断为天精度（0时0分0秒0毫秒）系统日
	 * 
	 * @param date 系统日期，非空。
	 * @return 系统日，非空。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default java.sql.Date day(java.util.Date date) {
		return DateUtils.toZoneDate(date, getZoneOffset());
	}

	/**
	 * 本地日期自动截断为天精度（0时0分0秒0毫秒）系统日
	 * 
	 * @param localDate 本地日期，非空。
	 * @return 系统日，非空。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default java.sql.Date day(LocalDate localDate) {
		return DateUtils.toZoneDate(localDate, getZoneOffset());
	}

	/**
	 * 系统日期自动截断为天精度（0时0分0秒0毫秒）系统日期
	 * 
	 * @param localDate 系统日期，非空。
	 * @return 系统日期，非空。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default java.util.Date toDay(java.util.Date date) {
		long real = date.getTime();
		long expect = dayInMillis(real);
		return real != expect ? new java.util.Date(expect) : date;
	}

	/**
	 * 本地日期自动截断为天精度（0时0分0秒0毫秒）系统日期
	 * 
	 * @param localDate 本地日期，非空。
	 * @return 系统日期，非空。
	 * @throws NullPointerException 非空参数为空
	 * @since 1.0
	 */
	default java.util.Date toDay(LocalDate localDate) {
		long real = DateUtils.toEpochMilli(localDate, getZoneOffset());
		return new java.util.Date(real);
	}

}
